from constants import *
from soldier import *
from ship import *
from tools_name_generator import *
import random
from math import sin, cos, pi, atan2, sqrt, atan


class PlanetArea(object):
    def __init__(self,planet,x,y):
    
        self.isCleared = False
        
        self.x = x
        self.y = y
        self.planet = planet
        self.building = BUILDING_NONE
        
        # TODO:
        # generate yeld and initial amount of minerals and organics 
        # using planet information (temp, type etc) and coodrinates (latitude?)
        self.minerals = random.randint(0,50)
        self.organics = random.randint(0,50)
        self.mineralYeld = random.randint(0,5)
        self.organicYeld = random.randint(0,5)    
        
    
    def clear(self,firstLanding=False):
        if self.isCleared == False:
            # the firstLanding flag is used when a ship arrives to an
            # neutral planet for the first time, and "burns" an area
            if firstLanding == False:
                self.planet.minerals += self.minerals
                self.planet.organics += self.organics    
            self.minerals = 0
            self.organics = 0
            self.isCleared = True
        
        

class Planet(object):

    def __init__(self,seed):
        
        self.seed = seed
        
        self.position = (0,0)
        
        self.size = int(abs(random.gauss(0, 3.2)))+PLANET_MIN_SIZE
        if self.size > PLANET_MAX_SIZE: self.size = PLANET_MAX_SIZE
        
        self.type = random.randint(0,PLANET_TYPES-1)
        self.temperature = (self.type*10)+int(abs(random.gauss(0, 5)))
        self.name = generatePlanetName(self)
        
        # generate areas
        self.areas = {}
        self.productiveAreasCount = 0
        self.farmsCount = 0
        self.minesCount = 0
        self.housesCount = 0
        self.hasBase = False
        for y in range(self.size):
            for x in range(self.size):
                self.areas[(x,y)] = PlanetArea(self,x,y)
                
        self.popTimer = POPULATION_TIMER
        # TODO: this is the minimum population that can survive without houses on the planet
        # it is calculated on the basis of "type" but can be improved
        self.minPopulationSize = 16-abs(self.type - 2)*2
        self.population = 0
        
        self.minerals = 0
        self.organics = 0
        
        self.soldiers = []
        self.trainingSoldiersCount = 0
        self.trainedSoldiersCount = 0

        self.ships = []
        self.dockedShips = []
        self.flyingShips = []
        self.buildingShips = []
        
        self.attackingShip = None
        self.underAttack = False
        self.inBattle = False
        self.productionOverlay = False
        
        self.owner = OWNER_NONE
      
        
    def update(self):
        if self.inBattle == True: return
        self._updatePopulation()
        self._updateProduction()
        self._updateSoldiers()
        self._updateShips()


    def _updatePopulation(self):
        
        # IDEA: population increase/decrease could be influenced by planet type and/or temperature
 
        # if there is enough space in the houses the population increases
        # note that the soldiers count as population (but they don't work :) )
        if (self.housesCount * HOUSE_CAPACITY) > (self.population + len(self.soldiers)):
            #print "Planet " + self.name + " - Population is increasing"
            self.popTimer -= 1
            if self.popTimer <= 0:
                #self.population += self.housesCount    
                self.population += 1 
                self.popTimer = POPULATION_TIMER
        # if houses are not sufficient, the population shrinks
        elif (self.housesCount * HOUSE_CAPACITY) < (self.population + len(self.soldiers))-self.minPopulationSize:
            #print "Planet " + self.name + " - Population is shrinking"
            self.popTimer -= 1
            if self.popTimer <= 0:
                self.population -= 1     
                self.popTimer = POPULATION_TIMER      
                      
                
    def _updateProduction(self):
        
        availableWorkers = self.population - self.hasBase

        for area in sorted(self.areas.values(), key=lambda x: random.random()):
            if availableWorkers <= 0:
                break
            if area.building == BUILDING_FARM:
                self.organics += area.organicYeld
                availableWorkers -= 0
            elif area.building == BUILDING_MINE:
                self.minerals += area.mineralYeld
                availableWorkers -= 0
            
        # clamp the maximum amount of minerals and organics
        # if the planet has a base this capacity is 10 fold
        minerals_capacity = MAX_STORED_MINERALS+MAX_STORED_MINERALS*self.hasBase*9
        organics_capacity = MAX_STORED_ORGANICS+MAX_STORED_ORGANICS*self.hasBase*9
        if self.minerals > minerals_capacity:
            self.minerals = minerals_capacity
        if self.organics > organics_capacity:
            self.organics = organics_capacity
            
                           
    def _updateSoldiers(self):
        # cycle through all the soldiers and update the training ones
        trainingSoldiersCount = 0
        trainedSoldiersCount = 0
        
        for soldier in self.soldiers:
            if soldier.isTraining == True:
                trainingSoldiersCount += 1
                soldier.power += SOLDIER_TRAINING_RATE
                if int(soldier.power) >= SOLDIER_MAX_POWER:
                    soldier.isTraining = False
                    soldier.power = SOLDIER_MAX_POWER
            else:
                trainedSoldiersCount += 1
                
        self.trainingSoldiersCount = trainingSoldiersCount
        self.trainedSoldiersCount = trainedSoldiersCount
        
        
    def _updateShips(self):
        # cycle through all the ships and update them
        self.dockedShips = []
        self.flyingShips = []
        self.buildingShips = []
        
        for ship in self.ships:
            ship.update()
            if ship.status == SHIP_STATUS_BUILDING: self.buildingShips += [ship]
            if ship.status == SHIP_STATUS_DOCKED: self.dockedShips += [ship]
            if ship.status == SHIP_STATUS_FLYING: self.flyingShips += [ship]

                
    def _shipArrival(self,ship):

        ship.transferOwnership(self)
        
        # check if the planet is empty or 
        if self.owner == OWNER_NONE or ship.owner == self.owner:
                     
            ship.dropContent(self)
            
            if self.owner == OWNER_NONE:
                # clear random area
                print len(self.areas)
                random.choice(self.areas.values()).clear(firstLanding=True)
                
                #the planet is conquered by ship.owner
                self.owner = ship.owner
                print "Planet " + self.name + " is now conquered by " + OWNER_NAMES[self.owner]
                
        # battle ensues
        else:
            self.attackingShip = ship
            self.underAttack = False
            self.inBattle = True        
          
          
    def solveBattle(self,winner=None):
        
        if winner == None:
            winner = random.choice([OWNER_PLAYER,OWNER_ENEMY])
            
        if winner == self.owner:
            pass
        else:
            #the planet is conquered by attackingShip.owner
            self.owner = self.attackingShip.owner
            print "Planet " + self.name + " is now conquered by " + OWNER_NAMES[self.attackingShip.owner]    
            self.soldiers = self.attackingShip.soldiers
            
            # TODO: decide what exactly happens when a planet is taken
            # ex. all the mines are destroyed etc.
    
        self.attackingShip = None
        self.inBattle = False
            
    
    def buildShip(self):
        # check that there is a base on this planet
        if self.hasBase == False: return "You need a base to build ships"
        
        # check that we have enough people
        if self.population < SHIP_BUILDING_PEOPLE_COST: return "You need a at least 5 people to build ships"
        
        # check if we have enough minerals
        if self.minerals < SHIP_BUILDING_MINERALS_COST: return "Not enough minerals"

        self.ships.append(Ship(self))

        # TODO: clamp the maximum amount of ships
        
        return True
    
        
    def launchShip(self,destination,population,organics, minerals, soldierNumber):   
        # try to launch the first available ship    
        ret = ""
        for ship in self.ships:
            ret = ship.launch(destination,population,organics, minerals, soldierNumber)
            if ret == True: return True
   
        return "Not suitable ship was found: " + ret
        
        
    def addSoldier(self):
        # check that there is a base on this planet
        if self.hasBase == False: return "You need a base to train soldiers"
        
        # check that we have enough people
        if self.population < 2: return "You need a at least 2 people to train soldiers"

        self.population -= 1
        self.soldiers.append(Soldier(self.owner))
            
        # TODO: subtract the cost       
        # TODO: clamp the maximum amount of soldiers
        
        return True
        
        
    def build(self,building_type,area):
    
        # check if is there some problem with the area parameter
        # (should always belong to the planet)
        if area not in self.areas.values(): return "Something wrong hapened with planet areas!"
        
        # check if the area is clear
        if not area.isCleared: return "Area must be cleared before building"
    
        # check if is there some problem with the owner parameter
        # (it should always be player or enemy)
        if self.owner == OWNER_NONE: return str(OWNER_NAMES[self.owner]) + " can not perform the building action"
    
        # check if there are enough resources
        if self.minerals < BUILDING_MINERAL_COSTS[building_type]: return "Not enough minerals"
    
        # check if the area is empty
        if area.building != BUILDING_NONE: return "There is already a building in this area"
        
        # check that we have enough people
        if self.population < BUILDING_PEOPLE_COSTS[building_type]: return "You need a at least "+str(BUILDING_PEOPLE_COSTS[building_type])+" people to build"
        
        # we cannot build more than one base
        if building_type == BUILDING_BASE:
            if self.hasBase == True:
                return "There is already a base on this planet, owned by " + str(self.owner)
            else:
                self.hasBase = True
                                    
        elif building_type == BUILDING_HOUSE:     
            self.housesCount += 1       
        elif building_type == BUILDING_FARM:
            self.farmsCount += 1
        elif building_type == BUILDING_MINE:
            self.minesCount += 1 
            
        # build it and subtract the cost
        area.building = building_type
        self.minerals -= BUILDING_MINERAL_COSTS[building_type]
            
        # print what happened :)
        print "Planet " + self.name + " - "+BUILDING_NAMES[building_type]+" built on area x:" + str(area.x) + " y:" + str(area.y)
    
        return True
        
        

        
        
        
        
